package com.yuxl.common.auth.service;

import com.yuxl.common.auth.entity.AuthMenu;
import com.yuxl.common.auth.repository.AuthMenuRepository;
import com.yuxl.common.core.domain.R;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author : yuxl
 * @create 2025/7/22 下午5:09
 */
@Slf4j
@Service
public class AuthMenuService {
    @Resource
    private AuthMenuRepository authMenuRepository;

    public Flux<AuthMenu> findAll() {
        return authMenuRepository.findAll();
    }

    public Mono<AuthMenu> findById(String name) {
        return authMenuRepository.findById(name)
                .defaultIfEmpty(new AuthMenu());
    }

    public Mono<Void> deleteById(String name) {
        return authMenuRepository.deleteById(name);
    }

    public Mono<AuthMenu> create(AuthMenu user) {
        user.setCreateTime(LocalDateTime.now())
                .setUpdateTime(LocalDateTime.now());
        return authMenuRepository.save(user);
    }

    public Mono<Integer> count(AuthMenu user) {
        return authMenuRepository.count(user);
    }


    public Flux<AuthMenu> page(AuthMenu user, int page, int size) {
        long offset = (long) (page - 1) * size;
        return authMenuRepository.page(user, size, offset);
    }


    public Flux<AuthMenu> list(AuthMenu user) {
        return authMenuRepository.list(user);
    }

    public Mono<AuthMenu> updateById(AuthMenu menu) {
        return authMenuRepository.updateById(menu);
    }

    /**
     * 通过页面名称查询菜单树
     * @param menuNames
     * @return
     */
    public Mono<List<AuthMenu>> tree(List<String> menuNames) {
        return Mono.zip(authMenuRepository.findAll().collectList(),
                        authMenuRepository.findAllById(menuNames).collectList())
                .map(tuple -> {
                    List<AuthMenu> all = tuple.getT1();
                    List<AuthMenu> menus = tuple.getT2();
                    // 通过叶子节点，构建整树
                    return buildTree(all,menus);
                });
    }

    private List<AuthMenu> buildTree(List<AuthMenu> all, List<AuthMenu> menus){
        // 1. 将全部菜单转换为 Map 结构 (name -> menu)
        Map<String, AuthMenu> allMenuMap = all.stream()
                .collect(Collectors.toMap(AuthMenu::getName, Function.identity()));

        // 2. 收集需要包含的所有节点（叶子节点+所有祖先节点）
        Set<String> includedNames = new HashSet<>();
        for (AuthMenu menu : menus) {
            String currentName = menu.getName();
            // 向上遍历直到根节点
            while (currentName != null) {
                // 防止循环引用导致的死循环
                if (includedNames.contains(currentName)) break;

                includedNames.add(currentName);
                AuthMenu currentMenu = allMenuMap.get(currentName);
                if (currentMenu == null) break; // 数据库数据异常时的保护

                currentName = currentMenu.getSupName();
            }
        }
        // 3. 过滤出需要构建树的节点
        List<AuthMenu> filteredNodes = includedNames.stream()
                .map(allMenuMap::get)
                .filter(Objects::nonNull)
                .toList();
        // 4. 构建快速访问的临时 Map
        Map<String, AuthMenu> nodeMap = new HashMap<>();
        filteredNodes.forEach(node -> {
            nodeMap.put(node.getName(), node);
            // 确保 children 列表被初始化
            if (node.getChildren() == null) {
                node.setChildren(new ArrayList<>());
            }
        });
        // 5. 构建树形结构
        List<AuthMenu> roots = new ArrayList<>();
        for (AuthMenu node : filteredNodes) {
            String supName = node.getSupName();
            if (supName == null || !nodeMap.containsKey(supName)) {
                // 当前节点是根节点
                roots.add(node);
            } else {
                // 将当前节点挂载到父节点
                AuthMenu parent = nodeMap.get(supName);
                if (parent != null) {
                    parent.getChildren().add(node);
                }
            }
        }
        return roots;
    }
}
